package app;

import beans.Beer;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.List;
import java.util.Properties;
import java.util.Scanner;
import java.util.logging.Level;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.cli.Option;
import org.apache.commons.cli.OptionBuilder;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
import org.apache.commons.cli.PosixParser;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import xml.beer.BeerReader;
import xml.beer.BeerWriter;
import utils.ImageUtils;

public class BeerApplication {

    private static Logger logger;
    private static Options options;

    public static void main(String[] args) {

        options = new Options();
        logger = LoggerFactory.getLogger(BeerApplication.class);
        configureOptions();
        CommandLineParser parser = new PosixParser();
        CommandLine cmd;

        String command = "";
        Scanner keyboard = new Scanner(System.in);
        boolean exit = false;

        if (args != null && args.length > 0) {
            for (String a : args) {
                command += a + " ";
            }
        } else {
            System.out.println("Enter the command\n--help or -h for help");
            command = keyboard.nextLine();
        }

        while (!exit) {
            try {
                cmd = parser.parse(options, command.split(" "));
                if (cmd.hasOption("help") || cmd.hasOption("h")) {
                    showHelp();
                } else if (cmd.hasOption("quit") || cmd.hasOption("q")) {
                    exit = true;
                } else if (cmd.hasOption("all") || cmd.hasOption("a")) {
                    showAll();
                } else if (cmd.hasOption("new") || cmd.hasOption("n")) {
                    String[] values = cmd.getOptionValues("n");
                    if (values != null && values.length == 5) {
                        addBeer(values);
                    } else {
                        System.out.println("Invalid number of parameters. Are required 5 parameters");
                        logger.info("Invalid number of parameters.");
                    }
                } else if (cmd.hasOption("remove") || cmd.hasOption("r")) {
                    String[] values = cmd.getOptionValues("r");
                    if (values != null && values.length == 1) {
                        removeBeer(values);
                    } else {
                        System.out.println("Invalid number of parameters. Is required 1 parameter");
                        logger.info("Invalid number of parameters.");
                    }
                } else if (cmd.hasOption("show") || cmd.hasOption("s")) {
                    String value = cmd.getOptionValue("s");
                    if (value != null) {
                        showBeer(value);
                    } else {
                        System.out.println("Invalid number of parameters. Is required 1 parameter");
                        logger.info("Invalid number of parameters.");
                    }
                } else if (cmd.hasOption("showId") || cmd.hasOption("id")) {
                    String[] values = cmd.getOptionValues("id");
                    if (values != null && values.length == 1) {
                        showBeerById(values);
                    } else {
                        System.out.println("Invalid number of parameters. Is required 1 parameter");
                        logger.info("Invalid number of parameters.");
                    }
                } else if (cmd.hasOption("edit") || cmd.hasOption("e")) {
                    String[] values = cmd.getOptionValues("e");
                    if (values != null && values.length == 6) {
                        editBeer(values);
                    } else {
                        System.out.println("Invalid number of parameters. Are required 6 parameters");
                        logger.info("Invalid number of parameters.");
                    }
                } else {
                    System.out.println("Invalid command. Please verify the syntax and available commands and retype the command");
                    logger.info("Invalid command - " + command);
                }
                if (!exit) {
                    command = keyboard.nextLine();
                }
            } catch (ParseException ex) {
                System.out.println("An error ocurred while trying to parse the command. Verify the syntax and try again");
                logger.error("Error to parse the command - " + command, ex);
            }

        }

    }

    private static void showHelp() {
        HelpFormatter formatter = new HelpFormatter();
        formatter.printHelp("Beer application", options);
    }

    private static void configureOptions() {

        logger.info("Configuring options for CLI");

        OptionBuilder.withLongOpt("help").withDescription("Show help");
        Option optHelp = OptionBuilder.create("h");

        OptionBuilder.withLongOpt("quit").withDescription("Terminates the application");
        Option optQuit = OptionBuilder.create("q");

        OptionBuilder.withLongOpt("all").withDescription("Show all beers");
        Option optAll = OptionBuilder.create("a");

        OptionBuilder.withLongOpt("new").withDescription("New beer").hasArgs(5)
                .withArgName("name mark alcoholic_percentage description image");
        Option optNew = OptionBuilder.create("n");

        OptionBuilder.withLongOpt("remove").withDescription("Remove a beer").hasArg().withArgName("name");
        Option optRemove = OptionBuilder.create("r");

        OptionBuilder.withLongOpt("show").withDescription("Show the especified beer").hasArg();
        Option optShow = OptionBuilder.create("s");
        
        OptionBuilder.withLongOpt("showId").withDescription("Show the especified beer by id").withArgName("id");
        Option optShowId = OptionBuilder.create("id");

        OptionBuilder.withLongOpt("edit").withDescription("Edit the especified beer").hasArgs(6)
                .withArgName("actual_name new_name new_mark new_alcoholic new_description new_image");
        Option optEdit = OptionBuilder.create("e");

        options.addOption(optHelp);
        options.addOption(optQuit);
        options.addOption(optAll);
        options.addOption(optNew);
        options.addOption(optRemove);
        options.addOption(optShow);
        options.addOption(optShowId);
        options.addOption(optEdit);

    }

    private static void showAll() {
        BeerReader beerReader = new BeerReader();
        List<Beer> allBeers = beerReader.getAllBeers();
        System.out.println("List of all beers\n" + allBeers);
    }

    private static void addBeer(String[] values) {

        Float f;
        try {
            f = Float.parseFloat(values[2]);
        } catch (NumberFormatException e) {
            System.out.println("Invalid value for alcoholic percentage. Please type a decimal value");
            return;
        }
        File image = new File(values[4]);
        String msg = ImageUtils.validateIsImageAndExists(image);
        if (msg != null) {
            System.out.println(msg);
            return;
        }
        String imagePath = ImageUtils.copyFile(image);
        if (imagePath == null) {
            return;
        }

        Beer beer = new Beer();

        Long id = getId();

        if (id == null) {
            return;
        }
        beer.setId(id);
        beer.setName(values[0]);
        beer.setMark(values[1]);
        beer.setAlcoholicPercentage(f);
        beer.setDescription(values[3]);
        beer.setImage(imagePath);
        BeerWriter xmlWriter = new xml.beer.BeerWriter();
        xmlWriter.print(beer);
    }

    private static void removeBeer(String[] values) {
        BeerReader reader = new BeerReader();
        Beer b = reader.getBeerByName(values[0]);
        if (b != null) {
            BeerWriter writer = new BeerWriter();
            writer.remove(b);
        } else {
            System.out.println("Not found beer with name = " + values[0]);
        }
    }

    private static void showBeer(String value) {
        BeerReader reader = new BeerReader();
        Beer b = reader.getBeerByName(value);
        if (b != null) {
            System.out.println(b);
        } else {
            System.out.println("Not found beer with name = " + value);
        }
    }

    private static void editBeer(String[] values) {
        BeerReader reader = new BeerReader();
        Beer b = reader.getBeerByName(values[0]);
        if (b == null) {
            System.out.println("Not found beer with name =" + values[0]);
            return;
        }
        Float f;
        try {
            f = Float.parseFloat(values[3]);
        } catch (NumberFormatException e) {
            System.out.println("Invalid value for alcoholic percentage. Please type a decimal value");
            return;
        }
        File image = new File(values[5]);
        String msg = ImageUtils.validateIsImageAndExists(image);
        if (msg != null) {
            System.out.println(msg);
            return;
        }
        String imagePath = ImageUtils.copyFile(image);
        if (imagePath == null) {
            return;
        }
        Beer newBeer = new Beer();
        newBeer.setId(b.getId());
        newBeer.setName(values[1]);
        newBeer.setMark(values[2]);
        newBeer.setAlcoholicPercentage(f);
        newBeer.setDescription(values[4]);
        newBeer.setImage(imagePath);
        BeerWriter writer = new BeerWriter();
        writer.remove(b);
        writer.print(newBeer);
    }

    private static void showBeerById(String[] values) {
        try{
            long id = Long.parseLong(values[0]);
            BeerReader reader = new BeerReader();
            Beer beer = reader.getBeerById(id);
            if(beer != null){
                System.out.println(beer);
            }else{
                System.out.println("Not found beer with id = " + id);
            }
        }catch (NumberFormatException ex){
            System.out.println("Invalid value for id. Please type a numeric integer value");
        }
    }

    private static Long getId() {
        Properties props = new Properties();
        File propsFile = new File("src/xml/beer/sequence.properties");
        boolean needLoadPropsFile = true;

        if (!propsFile.exists()) {
            try {
                propsFile.createNewFile();
                props.load(new FileInputStream(propsFile));
                props.setProperty("lastId", "0");
                props.store(new FileOutputStream(propsFile), "Save the last beer id");
                needLoadPropsFile = false;
            } catch (IOException ex) {
                java.util.logging.Logger.getLogger(BeerApplication.class.getName()).log(Level.SEVERE, null, ex);
            }
        }

        FileInputStream file;
        try {
            if (needLoadPropsFile) {
                file = new FileInputStream(propsFile);
                props.load(file);
            }
            Long lastId = new Long(props.getProperty("lastId"));
            Long idToSave = lastId + 1;
            props.setProperty("lastId", (idToSave).toString());
            OutputStream fosProps = new FileOutputStream(propsFile);
            props.store(fosProps, "Save the last beer id");
            return lastId;
        } catch (FileNotFoundException ex) {
            logger.error("Sequence file not found", ex);
            return null;
        } catch (IOException ex) {
            logger.error("Error", ex);
            return null;
        }
    }
}
